Skip to content

release: goal-loop silent-stall fixes (GOAL-FP-01-17/18/19) - #287

Merged
LeXwDeX merged 4 commits into
mainfrom
dev
Aug 15, 2026
Merged

release: goal-loop silent-stall fixes (GOAL-FP-01-17/18/19)#287
LeXwDeX merged 4 commits into
mainfrom
dev

Conversation

@LeXwDeX

@LeXwDeX LeXwDeX commented Aug 15, 2026

Copy link
Copy Markdown
Owner

合并 dev 到 main,包含 goal-loop 静默卡死的止血修复:

#283 fix(goal): tolerate vanished-session message windows, surface loop failures

  • 根因:afterIdle 对消息窗口的读取在 session 行消失时抛 NotFoundError,被 fork 处裸 Effect.ignore 吞掉 → goal 永久 active、turns_used 恒 0、零日志(生产事故同形)
  • 三处消息窗口(pre-judge / 僵尸探针 / post-judge 重载)统一容错为空窗口
  • fork 点 Effect.ignorecatchCause + logWarning(interrupt 保持静默,F1 纪律)
  • judge 链 orElseSucceedcatchCause:provider 链 defect 并入 parseFailed 预算而非静默杀死评估
  • auto-pause transcript 行对称化;P2-B 注释更正(effect v4 Effect.ignore 实际吸收 defect)
  • 回归测试:生产装配探针(bootstrap-wiring)+ GOAL-FP-01-18/18b;lint 净增归零并清 11 处既有未用导入

#284 fix(goal): retry ESC pause persistence before giving up the goal

  • ESC 暂停遇瞬时 DB 失败不再静默丢失:重试两次(50ms 退避,Effect.exit 覆盖 defect),用尽后 logError 高亮

验证:两 PR 独立通过 dev 门禁(Typecheck + CodeQL);test/goal 103 pass + 生产装配探针 GREEN;请 main 全量门禁(Typecheck + Unit Tests linux + E2E linux/windows)复核。

遗留已立 issue(不阻塞发布):#285(崩溃重启轮数通胀,需 durable 边界设计)、#286(HookCommand 静默丢弃字段)。

…ilures

Root cause of the invisible goal stall: the afterIdle evaluation died on a
typed NotFoundError from the messages-window read (session row gone mid-goal,
or a synthetic session), and the fork's bare Effect.ignore swallowed it —
goal left permanently active, turns_used frozen at 0, zero logs.

- afterIdle + zombie probe + post-judge reload: NotFoundError on any of the
  three messages windows degrades to an empty window (MessageV2.stream
  pattern) so the existing visible branches handle it (pause / continuation)
- triggerEvaluation fork: Effect.ignore -> catchCause + logWarning (interrupts
  stay silent per F1) so future evaluation failures are diagnosable
- judge chain: orElseSucceed -> catchCause so DEFECTS in the production
  provider chain (config orDie, payload decode) fold into the parseFailed
  budget instead of silently killing the evaluation
- auto-pause transcript line: ignore -> catchCause + logWarning (symmetric
  with the done branch)
- P2-B comment corrected: effect v4 Effect.ignore absorbs defects too;
  catchCause's value here is diagnosability, not subscription survival
- regression tests: production-wiring probe (bootstrap-wiring.test.ts),
  pre-judge window pause, post-judge reload tolerance (GOAL-FP-01-18),
  judge-defect budget degradation (GOAL-FP-01-18b)
- lint: new code is warning-neutral; drop 11 pre-existing unused imports to
  restore gate margin (oxlint ratchet, local 4846 -> 4829)
fix(goal): tolerate vanished-session message windows, surface loop failures
A transient DB failure during pauseForUserCancel would silently lose the
pause: the turnDriven mark cleared, the pause never persisted, and the next
idle event resurrected the goal against the user's explicit ESC
(shouldPreempt cannot catch ESC — it adds no user message). Retry the pause
up to twice with 50ms backoff (Effect.exit captures defects, unlike typed
retry); on exhausted retries log loudly instead of warning, so a resurrecting
goal is never invisible.

No clean fault-injection seam for the retry branch (test DB is :memory:,
pauseAndPublish is a layer closure over drizzle orDie) — happy path covered
by test/goal/turn-scope.test.ts; seam absence recorded per diagnosis policy.
fix(goal): retry ESC pause persistence before giving up the goal
@LeXwDeX
LeXwDeX merged commit 93a475e into main Aug 15, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant